package searchurl;

import searchurl.common.TextFileTools;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

import java.util.Map;
import java.util.Set;
import searchurl.dto.QueryConditionDto;
import searchurl.excel.MsExcelTools;
import searchurl.model.SearchResult;

/**
 * be avoked by UI
 * @author iridiumcao
 */
public class SearchUrl {

    private Map<String, String> index = new HashMap<String, String>();

    /**
     *
     * @param searchDir
     *            the path of search destination directory
     * @param keywordFiles
     *            list of paths of keyword files
     * @return
     */
    private List<SearchResult> searchUrl(String searchDirPath,
            Set<String> keywordFiles, boolean restrict211) {
        Set<String> keywords = buildKeywords(keywordFiles);

        List<SearchResult> results = new ArrayList<SearchResult>();

        File searchDir = new File(searchDirPath);

        results = searchUrl(searchDir, keywords, restrict211);

        return results;
    }

    private List<SearchResult> searchUrl(File searchDir, Set<String> preFilterKeywords, Set<String> keywords, boolean restrict211) {
        List<SearchResult> results = new ArrayList<SearchResult>();
        File[] files = searchDir.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                results.addAll(searchUrl(file, preFilterKeywords, keywords, restrict211));
            } else if (file.isFile() && file.getName().matches("\\d+")) {
                SearchResult result = searchSingleFile(file, preFilterKeywords, keywords, restrict211);
                if (result.getUrl() != null && !(result.getUrl().equals(""))) {
                    results.add(result);
                }
            }
        }
        return results;
    }

    private List<SearchResult> searchUrl(File searchDir, Set<String> keywords, boolean restrict211) {
        List<SearchResult> results = new ArrayList<SearchResult>();
        File[] files = searchDir.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                results.addAll(searchUrl(file, keywords, restrict211));
            } else if (file.isFile() && file.getName().matches("\\d+")) {
                SearchResult result = searchSingleFile(file, keywords, restrict211);
                if (result.getUrl() != null && !(result.getUrl().equals(""))) {
                    results.add(result);
                }
            }
        }
        return results;
    }

    /**
     *
     * @param file
     *            destination file to search by keywords list
     * @param keywords
     *            list of keywords. As the result required to be classified by
     *            keyword, here cannot building all keywords in a single regular
     *            expression.
     * @return
     */
    private SearchResult searchSingleFile(File file, Set<String> keywords, boolean restrict211) {
        SearchResult result = new SearchResult();
        result.setKeywords(new HashSet<String>());

        String fileContent = buildFileString(file);

        if (restrict211) {
            if (fileContent.contains("211.136.165.53")) {
                for (String keyword : keywords) {
                    if (fileContent.contains(keyword)) {
                        result.getKeywords().add(keyword);
                    }
                }
                if (result.getKeywords().size() > 0) {
                    String url = findUrl(file);
                    if (url != null) {
                        result.setUrl(url);
                        result.setSite(url.substring(0, url.indexOf('/', 7)));
                    } else {
                        //TODO record log that index file was broken.
                    }

                }
            }
        } else {
            for (String keyword : keywords) {
                if (fileContent.contains(keyword)) {
                    result.getKeywords().add(keyword);
                }
            }
            if (result.getKeywords().size() > 0) {
                String url = findUrl(file);
                if (url != null) {
                    result.setUrl(url);
                    result.setSite(url.substring(0, url.indexOf('/', 7)));
                } else {
                    //TODO record log that index file was broken.
                }

            }
        }


        return result;
    }

    /**
     *
     * @param file
     *            destination file to search by keywords list
     * @param keywords
     *            list of keywords. As the result required to be classified by
     *            keyword, here cannot building all keywords in a single regular
     *            expression.
     * @return
     */
    private SearchResult searchSingleFile(File file, Set<String> preFilterKeywords, Set<String> keywords, boolean restrict211) {
        SearchResult result = new SearchResult();
        result.setPrefilters(new HashSet<String>());
        result.setKeywords(new HashSet<String>());

        String fileContent = buildFileString(file);
        if (restrict211) {
            if (fileContent.contains("211.136.165.53")) {
                boolean flag = false;
                for (String preKeyword : preFilterKeywords) {
                    if (fileContent.contains(preKeyword)) {
                        result.getPrefilters().add(preKeyword);
                        flag = true;
                    }
                }
                if (flag) {
                    for (String keyword : keywords) {
                        if (fileContent.contains(keyword)) {
                            result.getKeywords().add(keyword);
                        }
                    }
                    if (result.getKeywords().size() > 0) {
                        String url = findUrl(file);
                        if (url != null) {
                            result.setUrl(url);
                            result.setSite(url.substring(0, url.indexOf('/', 7)));
                        } else {
                            //TODO record log that index file was broken.
                        }
                    }
                }
            }
        } else {
            boolean flag = false;
            for (String preKeyword : preFilterKeywords) {
                if (fileContent.contains(preKeyword)) {
                    result.getPrefilters().add(preKeyword);
                    flag = true;
                }
            }
            if (flag) {
                for (String keyword : keywords) {
                    if (fileContent.contains(keyword)) {
                        result.getKeywords().add(keyword);
                    }
                }
                if (result.getKeywords().size() > 0) {
                    String url = findUrl(file);
                    if (url != null) {
                        result.setUrl(url);
                        result.setSite(url.substring(0, url.indexOf('/', 7)));
                    } else {
                        //TODO record log that index file was broken.
                    }
                }
            }
        }

        return result;
    }

    /**
     * find destination URL, which contained in the index file index.attr in the
     * same folder of this file, according to the file name
     *
     * @param file
     * @return
     */
    private String findUrl(File file) {
        String url = index.get(file.getPath());
        if (url == null) {
            this.buildIndex(new File(file.getParentFile(), "index.attr"));
            url = index.get(file.getPath());
        }
        return url;
    }

    /**
     * parse all keyword files to a Set<String> object
     *
     * @param keywordFiles
     *            list of paths of keyword files
     * @return list of all keywords
     */
    private Set<String> buildKeywords(Set<String> keywordFiles) {
        Set<String> keywords = new HashSet<String>();
        for (String keywordFilePath : keywordFiles) {
            keywords.addAll(parseSingleFile(keywordFilePath));
        }
        return keywords;
    }

    /**
     * 
     *extract all words in Excel files to a Set<String> object
     * 
     * @param path
     *      the MS Excel (2003/2007) file path
     * @return
     */
    private Set<String> parseSingleFile(String path) {
        Set<String> words = new HashSet<String>();
        if (path.endsWith(".xls") || path.endsWith(".xlsx")) {
            words = MsExcelTools.fetchWordsFromExcel(path);
        } else { //all other formats are consider as plain text to parse.
            words = TextFileTools.buildKeywordsFromPlainText(path);
        }
        return words;
    }

    /**
     *parse the index file "index.attr" to the Map object 'index'
     * @param file the file named "index.attr"
     */
    private void buildIndex(File file) {
        FileInputStream fis = null;
        InputStreamReader isr = null;
        BufferedReader bfr = null;
        try {
            //  String encoding = TextFileTools.getCodeString(indexFile.getCanonicalPath());
            fis = new FileInputStream(file);
            isr = new InputStreamReader(fis, "UTF-8");
            bfr = new BufferedReader(isr);
            String line = bfr.readLine();
            while (line != null && !line.equals("")) {
                line = line.trim();
                String[] array = line.split("\\t");
                index.put(file.getParent() + File.separator + array[0], array[1]);
                line = bfr.readLine();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bfr != null) {
                    bfr.close();
                }
                if (isr != null) {
                    isr.close();
                }
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * read a plain text file which coded in UTF8, and conbinate its content for a String object
     * @param file
     * @return
     */
    private String buildFileString(File file) {
        StringBuilder sb = new StringBuilder();
        FileInputStream fis = null;
        InputStreamReader isr = null;
        BufferedReader bfr = null;
        try {
            //String encoding = TextFileTools.getCodeString(file.getCanonicalPath());
            fis = new FileInputStream(file);
            isr = new InputStreamReader(fis, "utf-8");
            bfr = new BufferedReader(isr);
            String line = bfr.readLine();
            sb.append(line);
            while (line != null) {
                line = line.trim();
                sb.append(line);
                line = bfr.readLine();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bfr != null) {
                    bfr.close();
                }
                if (isr != null) {
                    isr.close();
                }
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return sb.toString();
    }

    public List<SearchResult> searchUrl(QueryConditionDto queryDto) {
        Set<String> keywords = buildKeywords(queryDto.getKeywordFiles());
        Set<String> searchDirPathes = queryDto.getSearchDirPathes();
        boolean restrict211 = queryDto.isRestrict211();

        List<SearchResult> results = new ArrayList<SearchResult>();

        for (String searchDirPath : searchDirPathes) {
            File searchDir = new File(searchDirPath);
            if (queryDto.isEnablePreFilter()) {
                Set<String> preFilterKeywords = parseSingleFile(queryDto.getPreFilterPath());
                results.addAll(searchUrl(searchDir, preFilterKeywords, keywords, restrict211));
            } else {
                results.addAll(searchUrl(searchDir, keywords, restrict211));
            }

        }
        return results;
    }
}